﻿using System;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using Rhino.Commons;
using Rhino.Commons.ForTesting;
using Rhino.Mocks;

/*
 * based on code from:
 * http://code.google.com/p/kym/source/browse/trunk/src/Kym.Testing/PersistenceTestsBase.cs
 * http://flux88.com/TheTransitionFromTDDToBDD.aspx
 */

namespace Quiziqal.Core.ForTesting
{
    /// <summary>
    /// This class serves as a base class for database related tests using NHibernate.
    /// </summary>
    /// <remarks>The tests are run using an in-memory SQLite database.
    public abstract class PersistenceTestBase : DatabaseTestFixtureBase 
    {
        protected PersistenceTestBase()
        {
            var forbiddenMethods = from method in GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
                                   where method.GetCustomAttributes(typeof(TestFixtureSetUpAttribute), false).Any() ||
                                         method.GetCustomAttributes(typeof(SetUpAttribute), false).Any() ||
                                         method.GetCustomAttributes(typeof(TearDownAttribute), false).Any()
                                   select method;

            if (forbiddenMethods.Count() > 3)
                throw new InvalidOperationException(
                    @"TestFixtureSetup, SetUp or TearDown attributes are used in the base test class. Override TestFixtureSetup, Setup and Teardown methods instead.");
        }

        [TestFixtureSetUp]
        public void InitializeNHibernate()
        {
            IntializeNHibernateAndIoC(PersistenceFramework.NHibernate,
                "windsor.config",
                MappingInfo.From(Assembly.LoadFrom("Quiziqal.Persistence.dll"))
                );

            TestFixtureSetup();
        }

        [SetUp]
        public void StartUnitOfWork()
        {
            CurrentContext.CreateUnitOfWork();

            EstablishContext();

            try
            {
                When();
            }
            catch (Exception exc)
            {
                ExceptionThrown = exc;
            }
        }

        [TearDown]
        public void DisposeUnitOfWork()
        {
            Teardown();

            CurrentContext.DisposeUnitOfWork();
        }

        protected static void FlushSessionAndEvict(object instance)
        {
            UnitOfWork.Current.TransactionalFlush();
            UnitOfWork.CurrentSession.Evict(instance);
        }

        protected virtual void TestFixtureSetup() { }
        protected virtual void Setup() { }
        protected virtual void Teardown() { }


        protected T Mock<T>() where T : class
        {
            return MockRepository.GenerateMock<T>();
        }

        protected T Stub<T>() where T : class
        {
            return MockRepository.GenerateStub<T>();
        }

        protected Exception ExceptionThrown { get; private set; }
        protected abstract void EstablishContext();
        protected abstract void When();
    }
}
